Redis基础数据结构–链表结构源码精读¶
adlist是一个存储双向链表的数据结构文件.¶
adlist.h(c) - A generic doubly linked list implementation¶
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。
作为一种常用数据结构,链表内置在很多高级的编程语言里面,因为Redis使用的C语言没有内置这种数据结构,所以Redis构建了自己的链表实现。
链表在Redis中的应用非常广泛,比如列表键的底层实现之一就是链表。
当一个列表键包含了数量比较多的元素,有或者列表中好汉的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现。
使用到链表的功能有列表键,发布于订阅,慢查询,监视器等,服务器本身还使用链表来保存多个客户端的状态信息,以及使用了链表来构建客户端输出缓冲区。
这里分析的版本是redis3.0,具体版本可在https://github.com/antirez/redis/tree/3.0下载
逐句分析:
#ifndef ADLIST_H #define ADLIST_H 这两句和头文件里的最后一句 #endif /* ADLIST_H */ 共同起到包含adlist.h并避免重复包含的作用。
一,数据结构部分:¶
listNode、list和listIter是当前链表使用的三种数据结构。
链表节点ListNode¶
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value; //通用实现,可以存放任意类型的数据
} listNode;
节点存储了三个指针变量:
分别用于指向前一节点,下一节点,指向自身储存的数据。
访问链表的迭代器listIter¶
// list迭代器
typedef struct listIter {
listNode *next;
int direction; //用于控制链表遍历的方向
} listIter;
存储了一个指针变量,一个int型变量:
*next用于指向链表中的某个节点,
direction表示迭代器访问的方向,与该变量匹配的有两个宏在h文件末尾,是
#define AL_START_HEAD 0
表示从头结点到尾节点的正向迭代,
#define AL_START_TAIL 1
表示从尾节点到头结点的逆向迭代。
链表结构list¶
// list数据结构
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr); //用于复制ptr值,实现深度复制
void (*free)(void *ptr); //释放对应类型结构的内存
int (*match)(void *ptr, void *key); //自定义匹配key
unsigned int len;
} list;
提供了 1,*head,tail两个节点指针分别指向链表的头部和尾部。 2,(*dup),(*free), (*match)三个函数指针。 *(*dup)用于复制链表中节点的值, (*free)用于释放链表中节点的值, (*match)用于匹配链表中节点的值,对比链表节点所保存的值和另一个输入值是否相等。 3,无符号整数变量len表示链表的长度。
二,函数声明与实现:¶
新建一个链表¶
list *listCreate(void);
* Create a new list. The created list can be freed with
* AlFreeList(), but private value of every node need to be freed
* by the user before to call AlFreeList().
* On error, NULL is returned. Otherwise the pointer to the new list. */
list *listCreate(void)
{
struct list *list;
if ((list = zmalloc(sizeof(*list))) == NULL)
return NULL;
list->head = list->tail = NULL;
list->len = 0;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list;
}
如果分配内存失败,返回空值;
如果分配内存成功:依次分配list各项的值。
返回链表。
这里面用到了zmalloc这个函数,这个函数包含于zmalloc.c这个文件中,是redis自己实现的函数。回头再发这个文件的分析。
链表释放。¶
void listRelease(list *list);
/* Free the whole list.
*
* This function can't fail. */
void listRelease(list *list)
{
unsigned int len;
listNode *current, *next;
current = list->head;
len = list->len;
while(len--) {
next = current->next;
if (list->free) list->free(current->value);//释放当前value占用的内存
zfree(current);//释放该节点结构体占用的内存空间
current = next;
}
zfree(list);
}
从链表头部开始递归至尾部,逐个释放节点。
值得注意的是
if (list->free) list->free(current->value);
这一句,关于函数指针的使用,我暂时没弄清楚list->free这个函数指针在哪里被赋的值。
向链表头插入一个新节点,值为value¶
list *listAddNodeHead(list *list, void *value);
/* Add a new node to the list, to head, contaning the specified 'value'
* pointer as value.
*
* On error, NULL is returned and no operation is performed (i.e. the
* list remains unaltered).
* On success the 'list' pointer you pass to the function is returned. */
list *listAddNodeHead(list *list, void *value)
{
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == 0) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = NULL;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
list->len++;
return list;
}
如果分配内存失败,返回NULL;
如果分配内存成功:
如果原链表为空链表,则头节点和尾节点都为新节点(原先都为NULL),
新节点的前一节点和下一节点都为空。
如果原链表不是空链表,则新节点前一节点为原链表尾节点,下一节点为NULL,
原尾节点的下一节点为新节点,将新节点赋值给尾节点。
链表长度加一,返回新链表。
向链表末尾插入一个新节点,值为value¶
list *listAddNodeTail(list *list, void *value);
/* Add a new node to the list, to tail, contaning the specified 'value'
* pointer as value.
*
* On error, NULL is returned and no operation is performed (i.e. the
* list remains unaltered).
* On success the 'list' pointer you pass to the function is returned. */
list *listAddNodeTail(list *list, void *value)
{
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == 0) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = list->tail;
node->next = NULL;
list->tail->next = node;
list->tail = node;
}
list->len++;
return list;
}
如果分配内存失败,返回NULL;
如果分配内存成功:
新节点赋值为value。
如果原链表为空链表,则头节点和尾节点都为新节点(原先都为NULL),
新节点的前一节点和下一节点都为空。
如果原链表不是空链表,则
新节点前一节点为原链表尾节点,
下一节点为NULL,
原尾节点的下一节点为新节点,将新节点赋值给尾节点。
链表长度加一,返回新链表。
链表中间插入节点¶
list *listInsertNode(list *list, listNode *old_node, void *value, int after);
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;//分配内存
node->value = value;
if (after) {
node->prev = old_node;//设置当前节点的前置节点为某个节点
node->next = old_node->next;//设置当前节点的后置节点为某个节点的后置节点
if (list->tail == old_node) {
list->tail = node; //如果old_node是链表尾部,则更新尾部
}
} else {//表示插入到某个节点之前
node->next = old_node; //设置当前节点的后置节点为某个节点
node->prev = old_node->prev;//设置当前节点的前置节点为某个节点的前置节点
if (list->head == old_node) {
list->head = node;//如果某个节点原本为链表头节点,更新链表头结点
}
}
if (node->prev != NULL) {
node->prev->next = node;//如果当前链表的前置节点不为空,则设置当前节点的前置节点的后置节点为当前节点
}
if (node->next != NULL) {
node->next->prev = node;//如果当前链表的后置节点不为空,则设置当前节点的后置节点的前置节点为当前节点
}
list->len++;
return list;
}
在链表list中插入节点在指定节点old_node的前或后(取决于after的值,若after为NULL,则插在old_node前,反之插在old_node后),值为value。
如果分配内存失败,返回NULL;
如果分配内存成功;
如果是插入在指定节点后:
设置新节点的前置节点为指定节点;
设置新节点的后置节点为指定节点的后置节点;
如果指定节点是尾节点;
将尾节点指针指向新节点;
如果是插入在指定节点前:
设置新节点的后置节点为指定节点;
设置新节点的前置节点为制定节点的前置节点;
如果指定节点为头结点;
将头结点指针指向新节点;
如果新节点的前置节点不为空;
将其后置节点设为新节点;
如果新节点的后置节点不为空;
将其前置节点设为新节点;
链表长度加一,返回新链表。
从链表中删除给定节点¶
void listDelNode(list *list, listNode *node);
/* Remove the specified node from the specified list.
* It's up to the caller to free the private value of the node.
*
* This function can't fail. */
void listDelNode(list *list, listNode *node)
{
if (node->prev)
node->prev->next = node->next;
else
list->head = node->next;
if (node->next)
node->next->prev = node->prev;
else
list->tail = node->prev;
if (list->free) list->free(node->value);
zfree(node);
list->len--;
}
如果节点有前置节点;
将其前置节点的后置节点改为被删除节点的后置节点;
否则;
将头结点指针指向被删除节点的后置节点;
如果节点有后置节点;
将后置节点的前置节点改为被删除节点的前置节点;
否则;
将尾节点指针指向被删除节点的前置节点;
释放当前value占用的内存;
释放该节点结构体占用的内存空间;
为list创建一个迭代器iterator¶
listIter *listGetIterator(list *list, int direction);
/* Returns a list iterator 'iter'. After the initialization every
* call to listNext() will return the next element of the list.
*返回列表迭代器“iter”。初始化之后,对listNext()的每个调用都将返回列表的下一个元素。
* This function can't fail. */
listIter *listGetIterator(list *list, int direction)
{
listIter *iter;
if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
if (direction == AL_START_HEAD)
iter->next = list->head;
else
iter->next = list->tail;
iter->direction = direction;
return iter;
}
申请内存;
如果迭代方向为正;
将迭代器指向头结点;
如果迭代方向为逆;
迭代器指向尾节点;
返回迭代器iter;
listNode *listNext(listIter *iter);//返回迭代器iter指向的当前节点并更新iter
返回迭代器的下一个元素¶
/* Return the next element of an iterator.
* It's valid to remove the currently returned element using
* listDelNode(), but not to remove other elements.
*
* The function returns a pointer to the next element of the list,
* or NULL if there are no more elements, so the classical usage patter
* is:
*
* iter = listGetIterator(list,<direction>);
* while ((node = listNext(iter)) != NULL) {
* doSomethingWith(listNodeValue(node));
* }
使用listDelNode()删除当前返回的元素是有效的,但不删除其他元素。
函数返回一个指向列表下一个元素的指针,如果没有其他元素,则返回空值,因此经典用法模式为:
* iter = listGetIterator(list,<direction>);
* while ((node = listNext(iter)) != NULL) {
* doSome thingWith(listNodeValue(node));
* }
* */
返回迭代器的下一个元素¶
listNode *listNext(listIter *iter)
{
listNode *current = iter->next;
if (current != NULL) {
if (iter->direction == AL_START_HEAD)
iter->next = current->next;
else
iter->next = current->prev;
}
return current;
}
如果迭代器现指的元素非空;
如果是正向迭代;
返回节点current为当前节点的后置节点;
如果是逆向迭代;
返回节点current为当前节点的前置节点;
返回节点current;
释放iter迭代器¶
void listReleaseIterator(listIter *iter); //释放iter迭代器
/* Release the iterator memory */
void listReleaseIterator(listIter *iter) {
zfree(iter);
}
链表复制¶
list *listDup(list *orig);//拷贝表头为orig的链表并返回
/* Duplicate the whole list. On out of memory NULL is returned.
* On success a copy of the original list is returned.
*
* The 'Dup' method set with listSetDupMethod() function is used
* to copy the node value. Otherwise the same pointer value of
* the original node is used as value of the copied node.
*
* The original list both on success or error is never modified. */
list *listDup(list *orig)
{
list *copy;
listIter *iter;
listNode *node;
if ((copy = listCreate()) == NULL) //创建一个表头
return NULL;
//设置新建表头的处理函数
copy->dup = orig->dup;
copy->free = orig->free;
copy->match = orig->match;
//迭代整个orig的链表,重点关注此部分。
//为orig定义一个迭代器并设置迭代方向;
iter = listGetIterator(orig, AL_START_HEAD);
//迭代器根据迭代方向不停迭代,相当于++it
while((node = listNext(iter)) != NULL) {
void *value;
//复制节点值到新节点
if (copy->dup) {//如果定义了list结构中的dup指针,则使用该方法拷贝节点值。
value = copy->dup(node->value);
if (value == NULL) {
listRelease(copy);
listReleaseIterator(iter);
return NULL;
}
} else
value = node->value;//获得当前node的value值
if (listAddNodeTail(copy, value) == NULL) { //将node节点尾插到copy表头的链表中
listRelease(copy);
listReleaseIterator(iter);
return NULL;
}
}
listReleaseIterator(iter);//自行释放迭代器
return copy;//返回拷贝副本
}
创建局部变量新链表copy,迭代器iter,链表节点node;
为复制的链表创建一个表头
复制链表的三个函数指针;
设置迭代器与迭代方向;
开始迭代;
如果定义有dup函数;
则调用dup函数进行值复制;
如果调用dup函数复制后的值为NULL;(个人思考是dup函数出错)
释放新复制的链表;
释放迭代器;
返回空值;
否则;
复制节点值value;
如果将新生成的node节点插入到copy尾部失败;(尾插入失败)
释放新复制的链表;
释放迭代器;
返回空值;(复制失败)
释放迭代器;
返回拷贝副本;
查找特定值的节点¶
listNode *listSearchKey(list *list, void *key); //在list中查找value为key的节点并返回
/* Search the list for a node matching a given key.
* The match is performed using the 'match' method
* set with listSetMatchMethod(). If no 'match' method
* is set, the 'value' pointer of every node is directly
* compared with the 'key' pointer.
*
* On success the first matching node pointer is returned
* (search starts from head). If no matching node exists
* NULL is returned. */
listNode *listSearchKey(list *list, void *key)
{
listIter *iter;
listNode *node;
iter = listGetIterator(list, AL_START_HEAD);
while((node = listNext(iter)) != NULL) {
if (list->match) {
if (list->match(node->value, key)) {
listReleaseIterator(iter);
return node;
}
} else {
if (key == node->value) {
listReleaseIterator(iter);
return node;
}
}
}
listReleaseIterator(iter);
return NULL;
}
创建迭代器,设置迭代方向为正;
迭代开始:
如果有匹配函数;
使用匹配函数进行匹配;
否则;
如果节点值与key值匹配;
释放迭代器;
返回节点;
释放迭代器;
返回NULL(没有匹配的项);
返回给定下标的节点;¶
listNode *listIndex(list *list, long index); //返回下标为index的节点地址
/* Return the element at the specified zero-based index
* where 0 is the head, 1 is the element next to head
* and so on. Negative integers are used in order to count
* from the tail, -1 is the last element, -2 the penultimate
* and so on. If the index is out of range NULL is returned.
* 在指定的基于零的索引处返回元素,
* 其中0是头,1是头旁边的元素,依此类推。
* 负整数用于从尾部计数,-1是最后一个元素,-2是倒数第二个元素,依此类推。
* 如果索引超出范围,则返回空值。
* */
listNode *listIndex(list *list, long index) {
listNode *n;
if (index < 0) {
index = (-index)-1;
n = list->tail;
while(index-- && n) n = n->prev;
} else {
n = list->head;
while(index-- && n) n = n->next;
}
return n;
}
如果下标小于零;
下标去相反数;
起始节点指向尾节点,逆向迭代直到匹配;
(这里没有用到迭代器,若下标超出范围,返回指向的最后一个节点的前置,即返回NULL)
否则;
起始节点指向头节点,正向迭代直到匹配;
(若下标超出范围,返回指向的最后一个节点的后置,返回NULL)
返回node;
在私有链表结构中创建正向迭代器¶
void listRewindTail(list *list, listIter *li); //将迭代器li重置为list的头结点并且设置为正向迭代
/* Create an iterator in the list private iterator structure */
void listRewind(list *list, listIter *li) {
li->next = list->head;
li->direction = AL_START_HEAD;
}
在私有链表结构中创建逆向迭代器¶
void listRewind(list *list, listIter *li); //将迭代器li重置为list的尾结点并且设置为逆向迭代
/* Create an iterator in the list private iterator structure */
void listRewindTail(list *list, listIter *li) {
li->next = list->tail;
li->direction = AL_START_TAIL;
}
将尾节点插到头结点¶
void listRotate(list *list);//将尾节点插到头结点
/* Rotate the list removing the tail node and inserting it to the head. */
/*旋转列表,移除尾部节点并将其插入头部。*/
void listRotate(list *list) {
listNode *tail = list->tail;
if (listLength(list) <= 1) return;
/* Detach current tail */
list->tail = tail->prev;
list->tail->next = NULL;
/* Move it as head */
list->head->prev = tail;
tail->prev = NULL;
tail->next = list->head;
list->head = tail;
}
创建局部变量tail保存尾节点;
如果链表长度小于等于一,直接返回,结束函数;
将尾指针指向尾节点的前置节点;
将现在尾指针指向的节点的后置节点设为NULL;
将头结点的前置节点设为tail;
将tail前置节点设为NULL;
头结点指针指向tail;
三,宏函数部分¶
#define listLength(l) ((l)->len)
//返回链表l长度,即节点数量
#define listFirst(l) ((l)->head)
//返回链表l的头结点地址
#define listLast(l) ((l)->tail)
//返回链表l的尾结点地址
#define listPrevNode(n) ((n)->prev)
//返回节点n的前置节点地址
#define listNextNode(n) ((n)->next)
//返回节点n的后置节点地址
#define listNodeValue(n) ((n)->value)
//返回节点n的节点值
#define listSetDupMethod(l,m) ((l)->dup = (m))
//设置链表l的复制函数为m方法
#define listSetFreeMethod(l,m) ((l)->free = (m))
//设置链表l的释放函数为m方法
#define listSetMatchMethod(l,m) ((l)->match = (m))
//设置链表l的比较函数为m方法
#define listGetDupMethod(l) ((l)->dup)
//返回链表l的赋值函数
#define listGetFree(l) ((l)->free)
//返回链表l的释放函数
#define listGetMatchMethod(l) ((l)->match)
//返回链表l的比较函数